
#pragma once


#include <cstdint>
#include <cstdlib>

#include "modpash_debug.hpp"

namespace modpash {


    /* TODO: 不同的协议PDU 结构也不同，FrameBuffer 应该由PDU 负责定义和管理，应用层只负责操作其中的ADU 部分,
    ADU 写入完成后，要向PDU 通知ADU 的结束位置，PDU 在此之后附加校验码  */

    enum class server_error : uint8_t {
        invalid_function = 0x01,
        invalid_address = 0x02,
        invalid_value = 0x03,
        server_failure = 0x04,
        acknowledge = 0x05,  // 从站收到了请求，但无法立即给出响应，主机应该稍后查询从站状态
        busy = 0x06,
    };

    constexpr uint8_t SERVER_ERROR_CODE_LIST[] = {
        static_cast<uint8_t>(server_error::invalid_function),
        static_cast<uint8_t>(server_error::invalid_address),
        static_cast<uint8_t>(server_error::invalid_value),
        static_cast<uint8_t>(server_error::server_failure),
        static_cast<uint8_t>(server_error::acknowledge),
        static_cast<uint8_t>(server_error::busy),
    };

    constexpr uint8_t SERVER_ERROR_CODE_MIN = static_cast<uint8_t>(server_error::invalid_function);
    constexpr uint8_t SERVER_ERROR_CODE_MAX = static_cast<uint8_t>(server_error::busy);


    class FrameBase {
       protected:
        uint8_t *_buf = nullptr;
        size_t _buf_len;
        const int _function_code;

        FrameBase(int function_code) :
            _function_code(function_code) {}

        void _assert_frame_buffer(size_t min_buf_len = 2) const {
            _modpash_assert_with_message(this->_buf != nullptr, "ADU 缓冲区不能为空指针");
            _modpash_assert_with_message(this->_buf_len >= min_buf_len, "ADU 缓冲区过小");
        }

       public:
        uint8_t function_code() const {
            return static_cast<uint8_t>(_function_code);
        }

        uint8_t *set_frame_buffer(uint8_t *adu_buffer, size_t len) {
            auto *tmp = this->_buf;
            this->_buf = adu_buffer;
            this->_buf_len = len;
            return tmp;
        }

        size_t get_frame_buffer_size() const {
            return this->_buf_len;
        }

        /**
         * @brief 检查缓冲区中ADU 数据帧的功能码是否正确
         *
         * @return int8_t 返回大于0，表示功能码符合对象类型，小于0 表示异常响应功能码，等于0 表示功能码不匹配
         */
        int8_t check_function_code() const {
            _assert_frame_buffer();

            if ((this->_buf[0] & 0x7f) != _function_code) {
                return 0;
            }

            return this->_buf[0];  // 异常响应功能码是在正常功能码最高位置1，也就是让符号位变成了1
        }
    };


    constexpr uint8_t low8(uint16_t v) {
        return static_cast<uint8_t>(v);
    }


    constexpr uint8_t high8(uint16_t v) {
        return static_cast<uint8_t>(v >> 8);
    }


    constexpr uint16_t u16_from_u8(uint8_t high, uint8_t low) {
        return (static_cast<uint16_t>(high) << 8) + low;
    }


    inline uint16_t u16_from_frame_field(uint8_t *buf, size_t index) {
        uint8_t h = buf[index];
        uint8_t l = buf[index + 1];
        return u16_from_u8(h, l);
    };


    inline void u16_to_frame_field(uint8_t *buf, size_t index, uint16_t value) {
        buf[index] = high8(value);
        buf[index + 1] = low8(value);
    }


    namespace _hide {

        uint16_t request_field_multi_value_address(const uint8_t *buf) {
            return u16_from_u8(buf[1], buf[2]);
        }


        uint16_t request_field_multi_value_count(const uint8_t *buf) {
            return u16_from_u8(buf[3], buf[4]);
        }


        template <typename ResponseXX>
        class ErrorResponse {
           public:
            static constexpr size_t MIN_ADU_SIZE = 2;


            size_t make_error_response(uint8_t error_code) {
                auto *t = static_cast<FrameBase *>(this);
                t->_assert_frame_buffer(MIN_ADU_SIZE);

                t->_buf[0] = t->function_code() | 0x80;
                t->_buf[1] = error_code;

                return MIN_ADU_SIZE;
            }


            uint8_t error_code() const {
                auto *t = static_cast<FrameBase *>(this);
                t->_assert_frame_buffer(MIN_ADU_SIZE);

                return t->_buf[1];
            }
        };


        template <typename RequestXX>
        class MultiReadRequest {
           public:
            static constexpr size_t MIN_ADU_SIZE = 5;


            size_t make_request(uint16_t value_address, uint16_t value_count) {
                auto *t = static_cast<FrameBase *>(this);
                t->_assert_frame_buffer(MIN_ADU_SIZE);

                // 功能码
                t->_buf[0] = t->function_code();

                // 起始地址
                u16_to_frame_field(t->_buf, 1, value_address);

                // 寄存器数
                u16_to_frame_field(t->_buf, 3, value_count);

                return MIN_ADU_SIZE;
            }


            uint16_t value_address() const {
                auto *t = static_cast<FrameBase *>(this);
                t->_assert_frame_buffer(MIN_ADU_SIZE);

                return u16_from_frame_field(t->_buf, 1);
            }


            uint16_t value_count() const {
                auto *t = static_cast<FrameBase *>(this);
                t->_assert_frame_buffer(MIN_ADU_SIZE);

                return u16_from_frame_field(t->_buf, 3);
            }
        };
    }  // namespace _hide


    class Frame01 : public FrameBase {
       public:
        Frame01() :
            FrameBase(0x01) {}
    };


    class Response01 : public Frame01, public _hide::ErrorResponse<Response01> {
       private:
        size_t _counter;

       public:
        size_t make_response() {
            // TODO:
            _modpash_assert_with_message(check_function_code() > 0, "需要正确的请求包以构造响应");
        }
    };


    class Request01 : public Frame01, public _hide::MultiReadRequest<Request01> {
       public:
        static constexpr size_t MIN_ADU_SIZE = _hide::MultiReadRequest<Request01>::MIN_ADU_SIZE;
    };

}  // namespace modpash